import logging
import pandas as pd
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
from scipy.stats import pearsonr
import statsmodels.api as sm
from tabulate import tabulate

def draw_network(
    G, title, save_path, ch_font, node_color='skyblue',
    edge_threshold=0.1, show_density=True, node_size_metric='betweenness'
):
    pos = nx.spring_layout(G, seed=42)
    edges = G.edges(data=True)
    weights = [max(abs(d['weight'])*20, 2) for (u, v, d) in edges]
    edge_colors = ['red' if d['weight'] > 0 else 'blue' for (u, v, d) in edges]

    # 节点大小：介数中心性
    if node_size_metric == 'betweenness':
        centrality = nx.betweenness_centrality(G)
    elif node_size_metric == 'degree':
        centrality = dict(G.degree())
    elif node_size_metric == 'eigenvector':
        centrality = nx.eigenvector_centrality(G)
    else:
        centrality = {n: 1 for n in G.nodes()}
    sizes = [centrality[n]*2000 + 400 for n in G.nodes()]

    fig = plt.figure(figsize=(8, 6))
    nx.draw_networkx_nodes(G, pos, node_color=node_color, node_size=sizes, edgecolors='white', linewidths=2)
    nx.draw_networkx_labels(G, pos, font_size=16, font_family=ch_font.get_name())
    nx.draw_networkx_edges(G, pos, width=weights, edge_color=edge_colors)
    edge_labels = {(u, v): f"{d['weight']:.2f}" for (u, v, d) in edges}
    nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=14)
    plt.title(title, fontproperties=ch_font, fontsize=14)
    plt.axis('off')

    # 图形空白处注释
    density = nx.density(G) if show_density else None
    annotation = (
        f"网络密度: {density:.3f}\n"
        f"节点大小: 介数中心性\n"
        f"边显示阈值: |相关系数| > {edge_threshold}, p < 0.05"
    )
    plt.text(0.01, 0.01, annotation, transform=fig.transFigure,
             fontsize=12, color='gray', verticalalignment='bottom', fontproperties=ch_font)

    fig.savefig(save_path, dpi=300, bbox_inches='tight')
    plt.close(fig)
    logging.info(f"{title} 已保存：{save_path}")

def calc_residuals(df_net, fields_core, control_vars):
    residuals = pd.DataFrame(index=df_net.index)
    for var in fields_core:
        y = pd.to_numeric(df_net[var], errors='coerce')
        X_raw = df_net[control_vars]
        X = pd.get_dummies(X_raw, drop_first=True)
        for col in X.columns:
            if X[col].dtype == 'bool':
                X[col] = X[col].astype(float)
        X = X.apply(pd.to_numeric, errors='coerce')
        X = X.fillna(0)
        X = sm.add_constant(X)
        model = sm.OLS(y, X, missing='drop').fit()
        residuals[var] = model.resid
    return residuals

def build_network(data, fields_core, core_name_map, threshold=0.1, pval_cut=0.05):
    G = nx.Graph()
    for var in fields_core:
        G.add_node(core_name_map[var])
    for i in range(len(fields_core)):
        for j in range(i+1, len(fields_core)):
            x = data[fields_core[i]]
            y = data[fields_core[j]]
            corr, pval = pearsonr(x, y)
            if pval < pval_cut and abs(corr) > threshold:
                G.add_edge(core_name_map[fields_core[i]], core_name_map[fields_core[j]], weight=corr)
    return G

def print_node_metrics(G, title):
    degree_dict = dict(G.degree())
    betweenness_dict = nx.betweenness_centrality(G)
    eigenvector_dict = nx.eigenvector_centrality(G)
    closeness_dict = nx.closeness_centrality(G)
    table = []
    for node in G.nodes():
        table.append([
            node,
            degree_dict[node],
            f"{betweenness_dict[node]:.3f}",
            f"{eigenvector_dict[node]:.3f}",
            f"{closeness_dict[node]:.3f}"
        ])
    headers = ["变量", "度数", "介数中心性", "特征向量中心性", "接近中心性"]
    print(f"\n{title}节点指标：")
    print(tabulate(table, headers, tablefmt="grid"))

def get_network_overall_metrics(G):
    metrics = {}
    metrics["网络密度"] = f"{nx.density(G):.3f}"
    metrics["平均度数"] = f"{np.mean([d for n, d in G.degree()]):.2f}"
    metrics["连通分量数量"] = nx.number_connected_components(G)
    if nx.is_connected(G) and len(G) > 1:
        metrics["平均路径长度"] = f"{nx.average_shortest_path_length(G):.2f}"
    return metrics

def print_network_overall_metrics(G, title):
    metrics = get_network_overall_metrics(G)
    # 指标做列，数值做行
    table = [list(metrics.values())]
    headers = list(metrics.keys())
    print(f"\n{title}整体指标：")
    print(tabulate(table, headers, tablefmt="grid"))
def fill_mode(df, columns):
    for col in columns:
        if df[col].dtype == 'object' or df[col].dtype.name == 'category':
            mode = df[col].mode()
            df[col] = df[col].fillna(mode[0] if not mode.empty else 0)
        else:
            median = df[col].median()
            df[col] = df[col].fillna(median)
    return df

def preprocess_city_df(df):
    df = df[df["城乡"].astype(str).str.strip() == "城市"].copy()
    mapping_dict = {
        "社会经济地位同龄比": {"较高": 3, "差不多": 2, "较低": 1, "不好说": 0},
        "社会经济地位三年前比": {"上升了": 3, "差不多": 2, "下降了": 1, "不好说": 0}
    }
    for col, mapping in mapping_dict.items():
        df[col] = df[col].map(mapping)
    if all(col in df.columns for col in mapping_dict):
        df["社会经济地位主观得分"] = df[list(mapping_dict.keys())].mean(axis=1)
    return df
